1 module hip.util.variant;
2 
3 enum Type : ubyte
4 {
5     undefined,
6     void_,
7     object,
8     string_,
9     bool_,
10     //Signed
11     byte_,
12     short_,
13     int_,
14     long_,
15     //Unsigned
16     ubyte_,
17     ushort_,
18     uint_,
19     ulong_,
20     //Floating
21     float_,
22     double_,
23     ///Untested
24     real_
25 }
26 
27 private union TypeUnion
28 {
29     string string_;
30     void* void_;
31     Object object;
32     bool bool_;
33     //Signed
34     byte byte_;
35     short short_;
36     int int_;
37     long long_;
38     //Unsigned
39     ubyte ubyte_;
40     ushort ushort_;
41     uint uint_;
42     ulong ulong_;
43     //Floating
44     float float_;
45     double double_;
46     ///Untested
47     real real_;
48 }
49 
50 pragma(inline, true)
51 pure Type getType(T)()  nothrow @nogc @safe
52 {
53     with(Type)
54     {
55         static if(is(T == string))
56             return string_;
57         else static if(is(T == void*))
58             return void_;
59         else static if(is(T : Object))
60             return object;
61         else static if(is(T == bool))
62             return bool_;
63         else static if(is(T == byte))
64             return byte_;
65         else static if(is(T == short))
66             return short_;
67         else static if(is(T == int))
68             return int_;
69         else static if(is(T == long))
70             return long_;
71         else static if(is(T == ubyte))
72             return ubyte_;
73         else static if(is(T == ushort))
74             return ushort_;
75         else static if(is(T == uint))
76             return uint_;
77         else static if(is(T == ulong))
78             return ulong_;
79         else static if(is(T == float))
80             return float_;
81         else static if(is(T == double))
82             return double_;
83         else static if(is(T == real))
84             return real_;
85         else static assert(false, "Unimplemented for type "~T.stringof);
86     }
87 }
88 
89 /**
90 *   Use that when you want to hold arbitrary defined type (which usually must be converted)
91 *   By using variant, your data will be converted only once and after that, it will be runtime
92 *   type strict.
93 */
94 struct Variant
95 {
96     Type type = Type.undefined;
97     TypeUnion data = void;
98 
99     string getTypeName() const @nogc
100     {
101         switch(type)
102         {
103             static foreach(mem; __traits(allMembers, Type))
104             {
105                 case __traits(getMember, Type, mem):
106                     return mem[0..$-1];
107             }
108             default:
109                 return "undefined";
110         }
111     }
112 
113     T get(T)() const
114     {
115         if(getType!T != type)
116         {
117             import hip.util.string;
118             SmallString s = SmallString("Tried to get a mismatching type: ", T.stringof, "(expected ", getTypeName, ")");
119             throw new Exception(s.toString);
120         }
121         return *cast(T*)(cast(void*)&data);
122     }
123     T set(T)(T value)
124     {
125         if(type == Type.undefined)
126             this.type = getType!T;
127         else if(type != getType!T)
128             throw new Exception("Tried to get a mismatching type: "~T.stringof~" (expected "~getTypeName~")");
129         data = *cast(TypeUnion)(cast(void*)&data);
130         return value;
131     }
132 
133     T opCast(T)() const
134     {
135         return get!T;   
136     }
137     bool opBinary(string op : "in")(const Type t) const
138     {
139         return type == t;
140     }
141     alias opBinaryRight = opBinary;
142 
143     static Variant make(string data)
144     {
145         return Variant(Type.string_, cast(TypeUnion)data);
146     }
147     
148     static Variant make(T)(T data) if(!is(T == string))
149     {
150         Variant v = void;
151         v.type = getType!T;
152         v.data = *cast(TypeUnion*)(cast(void*)&data);
153         return v;
154     }
155 
156     static Variant make(T)(string data)
157     {
158         import hip.util.conv:to;
159         return make!T(to!T(data));
160     }
161 }